// ml278
// Copyright (C) 2008 Bjørn Øivind Bjørnsen, Aleksander Gjermundsen, Knut Andre Tidemann, Thomas Hansen
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

#include "planetaryheightmapdata.h"
#include "planetaryscenemanager.h"
#include "planetaryoptions.h"
#include "planetarycommon.h"

#include <OgreString.h>
#include <OgreDataStream.h>
#include <fstream>

#define HEIGHTMAPVERTEXSIZE 2

namespace Ogre
{
	PlanetaryHeightmapData::PlanetaryHeightmapData(PlanetarySceneManager *mgr)
	{
		mSceneMgr = mgr;
		mOptions = mgr->getOptions();
	}

	PlanetaryHeightmapData::~PlanetaryHeightmapData()
	{
		clear();
	}

	void PlanetaryHeightmapData::clear()
	{
		for(std::list<HEIGHTMAPDATA *>::iterator itr = mCachedHeightmaps.begin(); itr != mCachedHeightmaps.end(); itr++)
		{
			delete[] (*itr)->heightmap;
			delete *itr;
		}
		mCachedHeightmaps.clear();

		for(std::list<ZONEHEIGHTMAPDATA *>::iterator itr = mCachedZoneHeightmaps.begin(); itr != mCachedZoneHeightmaps.end(); itr++)
		{
			delete[] (*itr)->heightmap;
			delete *itr;
		}

		mCachedZoneHeightmaps.clear();
	}

	const PlanetaryPageHeightmap *PlanetaryHeightmapData::getHeightmap(unsigned int planet,unsigned int zone, unsigned int pageX, unsigned int pageZ, unsigned int LOD) const
	{
		// Search for the heightmap in the cache
		for(std::list<HEIGHTMAPDATA *>::iterator itr = mCachedHeightmaps.begin(); itr != mCachedHeightmaps.end(); itr++)
		{
			if(	(*itr)->planet == planet &&
				(*itr)->zone == zone &&
				(*itr)->pageX == pageX &&
				(*itr)->pageZ == pageZ &&
				(*itr)->LOD == LOD)
			{
				return (*itr)->heightmap;
			}
		}

		// Heightmap not found in cache
		// Load from file
		String filename = mOptions->mHeightmapName;

		switch(zone)
		{
		case ZONE_UP:
			filename.append("_up_");
			break;

		case ZONE_DOWN:
			filename.append("_dn_");
			break;

		case ZONE_FRONT:
			filename.append("_fr_");
			break;

		case ZONE_BACK:
			filename.append("_bk_");
			break;

		case ZONE_LEFT:
			filename.append("_lf_");
			break;

		case ZONE_RIGHT:
			filename.append("_rt_");
			break;

		default:
			throw new Ogre::Exception(0,"Invalid zone id: " + StringConverter::toString(zone),__FILE__ + StringConverter::toString(__LINE__));
		}

		if(LOD != 0)
			filename.append(String("lod") + StringConverter::toString(LOD) + "_");

		char nr[5] = { 0 };
		sprintf(nr,"%04d", mOptions->ZoneSize * pageZ + pageX + 1);

		filename.append(nr);

		DataStreamPtr datastream(ResourceGroupManager::getSingleton().openResource(filename));

		if(datastream.isNull())
			return NULL;
        
		unsigned int read = 0;
		unsigned int bufsize = (mOptions->PageSize * (mOptions->TileSize-1) + 1) *
			(mOptions->PageSize * (mOptions->TileSize-1) + 1);

		float *heightmap = new float[bufsize];

		unsigned short buf;

		while(!datastream->eof() && read < bufsize)
		{
			datastream->read(&buf,sizeof(short));

			heightmap[read++] = static_cast<float>(buf);
		}
		
		HEIGHTMAPDATA *info = new HEIGHTMAPDATA;
		info->planet = planet;
		info->zone = zone;
		info->pageX = pageX;
		info->pageZ = pageZ;
		info->LOD = LOD;
		info->heightmap = new PlanetaryPageHeightmap(mSceneMgr,heightmap,bufsize);

		mCachedHeightmaps.push_back(info);

		return info->heightmap;
	}

	const PlanetaryZoneHeightmap *PlanetaryHeightmapData::getZoneHeightmap(unsigned int planet, unsigned int zone) const
	{
		// Search for the heightmap in the cache
		for(std::list<ZONEHEIGHTMAPDATA *>::iterator itr = mCachedZoneHeightmaps.begin(); itr != mCachedZoneHeightmaps.end(); itr++)
		{
			if(	(*itr)->planet == planet &&
				(*itr)->zone == zone)
			{
				return (*itr)->heightmap;
			}
		}

		// Heightmap not found in cache
		// Load from file
		String filename = mOptions->mHeightmapName;

		switch(zone)
		{
		case ZONE_UP:
			filename.append("_up_");
			break;

		case ZONE_DOWN:
			filename.append("_dn_");
			break;

		case ZONE_FRONT:
			filename.append("_fr_");
			break;

		case ZONE_BACK:
			filename.append("_bk_");
			break;

		case ZONE_LEFT:
			filename.append("_lf_");
			break;

		case ZONE_RIGHT:
			filename.append("_rt_");
			break;
		}

		filename.append("lod2");

		DataStreamPtr datastream(ResourceGroupManager::getSingleton().openResource(filename));

		if(datastream.isNull())
			return NULL;
        
		unsigned int read = 0;
		unsigned int bufsize = (mOptions->mZoneSizeLOD1 * mOptions->mZoneSizeLOD1);

		float *heightmap = new float[bufsize];

		unsigned short buf;

		while(!datastream->eof() && read < bufsize)
		{
			datastream->read(&buf,sizeof(short));

			heightmap[read++] = static_cast<float>(buf);
		}
		
		ZONEHEIGHTMAPDATA *info = new ZONEHEIGHTMAPDATA;
		info->planet = planet;
		info->zone = zone;
		info->heightmap = new PlanetaryZoneHeightmap(mSceneMgr,heightmap,bufsize);

		mCachedZoneHeightmaps.push_back(info);

		return info->heightmap;
	}
}
